#include "crossdevice.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "util.h"



CrossDevice::CrossDevice()
{
	this->_crossDevice = NULL;
	this->_serialDevice = NULL;
	this->_udpDevice = NULL;
	this->_speed = 115200;
	this->_parity = 'n';
}
CrossDevice::~CrossDevice()
{
	if(this->_crossDevice)
	{
		this->close();
	}
	if(this->_serialDevice)
	{
		this->close();
	}
	if(this->_udpDevice)
	{
		this->close();
	}
}




CrossDeviceList* CrossDevice::getDeviceList(void)
{
	struct hid_device_info *devs, *cur_dev;
	CrossDeviceList* result = NULL;
	CrossDeviceList* current = NULL;
	char portName[8];
	int i;
	
	// get com list
	for(i = 1; i < 20; i++)
    {
        sprintf(portName, "COM%d", i);
		
        if(CrossPlatformSerial::exist(portName))
        {
			CrossDeviceList* temp = (CrossDeviceList*)malloc(sizeof(CrossDeviceList));
			temp->name = (char*)malloc(strlen(portName)+1);
			sprintf(temp->name, "%s", portName);
			temp->next = NULL;
			if(result == NULL)
			{
				result = temp;
				current = temp;
			}
			else
			{
				current->next = temp;
				current = temp;
			}
			
        }
    }
	
	// get hid device list
	devs = hid_enumerate(0x0, 0x0);
	cur_dev = devs;	
	while (cur_dev) {
		CrossDeviceList* temp = (CrossDeviceList*)malloc(sizeof(CrossDeviceList));
		char * product_string = WcharToChar(cur_dev->product_string);
		temp->name = (char*)malloc(strlen(product_string)+32);
		sprintf(temp->name, "VID=%04hx,PID=%04hx,%s", cur_dev->vendor_id, cur_dev->product_id, product_string);
		temp->next = NULL;
		if(result == NULL)
		{
			result = temp;
			current = temp;
		}
		else
		{
			current->next = temp;
			current = temp;
		}
		
		printf("Device Found\n  type: %04hx %04hx\n  path: %s\n  serial_number: %ls", cur_dev->vendor_id, cur_dev->product_id, cur_dev->path, cur_dev->serial_number);
		printf("\n");
		printf("  Manufacturer: %ls\n", cur_dev->manufacturer_string);
		printf("  Product:      %ls\n", cur_dev->product_string);
		printf("  Release:      %hx\n", cur_dev->release_number);
		printf("  Interface:    %d\n",  cur_dev->interface_number);
		printf("\n");
		cur_dev = cur_dev->next;
		
	}
	hid_free_enumeration(devs);
	
	
	
	// get udp list
	if(current == NULL)
	{
		result = UDPDevice::getDeviceList();
		current = result;
	}
	else
	{
		current->next = UDPDevice::getDeviceList();
	}
	return result;
}

bool CrossDevice::open(char* portName)
{
	if(strstr(portName, "VID"))
	{
		int vid, pid;
		this->close();
		sscanf(portName, "VID=%04x,PID=%04x", &vid, &pid);
		this->_crossDevice = hid_open(vid, pid, NULL);
		if (!this->_crossDevice) {
			printf("unable to open device\n");
			return false;
		}
		// Set the hid_read() function to be non-blocking.
		hid_set_nonblocking(this->_crossDevice, 1);
		return true;
	}
	if(strstr(portName, "COM"))
	{
		this->close();
		this->_serialDevice = new CrossPlatformSerial; 
		if(!this->_serialDevice->open(portName))
		{
			delete this->_serialDevice;
			this->_serialDevice = NULL;
			return false;
		}
		this->_serialDevice->setSettings(this->_speed, 8, 1, this->_parity);
		this->_serialDevice->setTimeouts(1, 1);
		return true;
	}
	if(strstr(portName, "UDP"))
	{
		this->close();

		this->_udpDevice = new UDPDevice();
		if(!this->_udpDevice->open(portName))
		{
			delete this->_udpDevice;
			this->_udpDevice = NULL;
			return false;
		}
		return true;
	}
	return false;
}

bool CrossDevice::close()
{
	if(this->_crossDevice)
	{
		hid_close(this->_crossDevice);
		this->_crossDevice = NULL;
	}
	if(this->_serialDevice)
	{
		this->_serialDevice->close();
		delete this->_serialDevice;
		this->_serialDevice = NULL;
	}
	if(this->_udpDevice)
	{
		this->_udpDevice->close();
		delete this->_udpDevice;
		this->_udpDevice = NULL;
	}
	return true;
}

void CrossDevice::setSpeed(int speed)
{
	this->_speed = speed;
	if(this->_serialDevice)
	{
		this->_serialDevice->setSettings(speed, 8, 1, this->_parity);
	}
}

void CrossDevice::setParity(char parity)
{
	this->_parity = parity;
	if(this->_serialDevice)
	{
		this->_serialDevice->setSettings(this->_speed, 8, 1, this->_parity);
	}
}

int CrossDevice::readBuf(char * buf, int bufSize)
{
	if(this->_crossDevice)
	{
		int res = hid_read(this->_crossDevice, (unsigned char *)buf, bufSize);
		if (res < 0)
			printf("Unable to read()\n");
		return res;
	}
	if(this->_serialDevice)
	{
		return this->_serialDevice->readBuf(buf, bufSize);
	}
	if(this->_udpDevice)
	{
		return this->_udpDevice->readBuf(buf, bufSize);
	}
	
	
	
	
	return 0;
}

int CrossDevice::writeBuf(char * buf, int bufSize)
{
	if(this->_crossDevice)
	{
		//The first byte is the report number (0x1)
		char * sendBuf = (char *)malloc(bufSize+1);
		sendBuf[0] = 0x01;
		memcpy(sendBuf+1, buf, bufSize);
		
		int res = hid_write(this->_crossDevice, (unsigned char *)sendBuf, bufSize+1);
		if (res < 0) {
			printf("Unable to write()\n");
			printf("Error: %ls\n", hid_error(this->_crossDevice));
		}
		free(sendBuf);
		return res;
	}
	if(this->_serialDevice)
	{
		return this->_serialDevice->writeBuf(buf, bufSize);
	}
	if(this->_udpDevice)
	{
		return this->_udpDevice->writeBuf(buf, bufSize);
	}
	
	return 0;
}

sockaddr_in * CrossDevice::getRemoteAddr(void)
{
	if(this->_udpDevice)
	{
		return this->_udpDevice->getRemoteAddr();
	}
	return NULL;
}

char * CrossDevice::getRemoteIP(void)
{
	sockaddr_in * addr = this->getRemoteAddr();
	if(addr != NULL)
	{
		return inet_ntoa(addr->sin_addr);
	}
	else
	{
		return NULL;
	}
}